#include "auth_login_impl.h"
#include "auth_login_impl.inl"
#include "../ServerInfo.h"
#include <core/sharedata/login_data.h>
#include <core/QbLogger.h>
#include <qbmessage/include/message_kernel.h>
#include <qbkernel/include/qb_base.h>

#ifndef _MSC_VER
#include <arpa/inet.h>
#endif
#include "../auth_mac.h"
#include <QRegExp>

namespace qb
{
    int CAuthLoginImpl::sReloginTime[] = {10, 10, 10, 20, 20, 20, 60, 60, 60, 300, 300, 300, 1800, 1800, 1800, 3600, 3600, 3600};

    CAuthLoginImpl::CAuthLoginImpl()
        : m_expired(true)
        , m_try_to_expire(false)
    {
		m_TaskGroup.SetObserver(this);
        qbBase::getMessageSync()->RegisterMessageObserverTUS(this);
    }

    CAuthLoginImpl::~CAuthLoginImpl()
    {
		destory();
    }

    int32_t CAuthLoginImpl::destory()
    {
        // 删除登录任务
        m_TaskGroup.StopLoginSteps();

        //清理登录任务
        m_TaskGroup.DeleteTaskGroups();

		qbBase::getMessageSync()->DeRegisterMessageObserverTUS(this);
        qbBase::getNetworkService()->DeRegisterNetworkServiceObserver(loginData::instance().ConnectID(), this);
        // 断开当前的连接
        CloseConnect();

        stopReLoginTimer();

        return 0;
    }

	int32_t CAuthLoginImpl::login()
	{
        if (isLogined())
		{
			log_error("already loggedin");
			return -1;
		}

		if (isLoginning())
		{
			log_error("already loginning");
			assert(0);
			return -1;
		}

        LoginUserInfo lui = QuoteServerInfo::instance().userInfo();
		QString strMac = getHostMacAddress();
        strMac = strMac.replace(QRegExp("\\:"), "-");
		// 更新登录用户数据
        loginData::instance().UserInfo().strAccount     = lui.strUser;
        loginData::instance().UserInfo().strRawPassword = lui.strPwd;
        loginData::instance().UserInfo().strMac		    = strMac.toStdString();

        ServerInfo si = QuoteServerInfo::instance().currentServer();
        loginData::instance().SetServerLoginAddress(si.address, si.port);
		// 重置状态数据
        loginData::instance().SetCurrentStatus(eNetStatus_None);
        loginData::instance().ResetLoginData();
        loginData::instance().SetReLogin(false);

        return m_TaskGroup.StartLoginSteps();
	}

    int32_t CAuthLoginImpl::reLogin()
    {
        if (isLoginning())
        {
            return -1;
        }

        // 停止当前的登录任务
        loginData::instance().SetCurrentStatus(eNetStatus_None);
        loginData::instance().ResetLoginData();
        loginData::instance().SetReLogin(true);

        m_TaskGroup.StopLoginSteps();

        // 断开当前的连接
        CloseConnect();

        // 开始登录任务
        qbBase::getMessageSync()->DeRegisterMessageObserverTUS(this);
        qbBase::getMessageSync()->RegisterMessageObserverTUS(this);
        return m_TaskGroup.StartLoginSteps();
    }

    int32_t CAuthLoginImpl::manualReLogin()
    {
		stopReLoginTimer();
		startReLoginTimer(std::bind(&CAuthLoginImpl::onTimer, this));
        return reLogin();
    }

    bool CAuthLoginImpl::isLoginning()
    {
        return loginData::instance().IsLoginning();
    }

    bool CAuthLoginImpl::isLogined()
    {
        return loginData::instance().IsLoggedin();
    }

    bool CAuthLoginImpl::isReLogin()
    {
        return loginData::instance().IsReLogin();
    }

    int32_t CAuthLoginImpl::logout()
    {
        QB_CHECK_RETURN_VALUE(isLogined() || isLoginning(), -1);

		if (isLoginning())
		{
			// 断开当前的连接
			CloseConnect();

			// 停止当前的登录任务
            loginData::instance().SetCurrentStatus(eNetStatus_None);
            loginData::instance().ResetLoginData();

			m_TaskGroup.StopLoginSteps();
		}
		else
		{
            loginData::instance().SetCurrentStatus(eNetStatus_None);
            loginData::instance().ResetLoginData();
		}

		return 0;
    }

	STDMETHODIMP_(void) CAuthLoginImpl::OnNetData(char* pRecvData, int nDataLen)
	{
		OnRecvMsg(pRecvData, nDataLen);
	}

	STDMETHODIMP_(void) CAuthLoginImpl::OnNetStatus(int status)
	{
		OnRecvNetStauts(status);
	}

    STDMETHODIMP_(void) CAuthLoginImpl::OnFailed(THIS_ int32_t connect_id, int32_t error_id)
    {
        QB_CHECK_RETURN_VOID1(connect_id == loginData::instance().ConnectID());

        if (isLoginning())
        {
            return;
        }

        qbBase::getNetworkService()->NotifyDisconnect(connect_id);

        manualReLogin();
    }

	void CAuthLoginImpl::CloseConnect()
	{
        qbBase::getMessageKernel()->CloseConnect(loginData::instance().ConnectID());
	}

    void CAuthLoginImpl::OnTaskStep(qb::NetStatus uStep, short ret_code)
    {
		if (eNetStatus_ConnectingLogin == uStep || eNetStatus_ConnectingGW == uStep)
		{
            qbBase::getMessageKernel()->CloseConnect(loginData::instance().ConnectID());
			loginData::instance().ConnectID() = qbBase::getMessageKernel()->CreateConnect();
		}

        qbBase::getMessageKernel()->NotifyNetStatus(uStep, ret_code);
    }

    void CAuthLoginImpl::OnAllTaskFinished()
    {
        stopReLoginTimer();

        qbBase::getMessageSync()->DeRegisterMessageObserverTUS(this);
        qbBase::getNetworkService()->RegisterNetworkServiceObserver(loginData::instance().ConnectID(), this);
        qbBase::getMessageKernel()->NotifyNetStatus(eNetStatus_Done, 0);

        loginData::instance().SetCurrentStatus(eNetStatus_Done);

        if (loginData::instance().IsReLogin())
        {
            qbBase::getNetworkService()->NotifyRecover(loginData::instance().ConnectID());
        }
    }

    void CAuthLoginImpl::OnTaskGroupFailed(qb::NetStatus uStep, short ret_code)
    {
        switch (ret_code)
        {
        case eNetError_ReloginServer:
            {
                // 断开当前的网络连接
                CloseConnect();

                // 异步删除登录任务组对象
				m_TaskGroup.StopLoginSteps();

                // 重新开始登录任务
				m_TaskGroup.StartLoginSteps();
            }
			break;
        default:
            {
                // 断开当前的网络连接
                CloseConnect();

                // 重置状态数据
                loginData::instance().SetCurrentStatus(eNetStatus_None);
                loginData::instance().ResetLoginData();

                // 异步删除登录任务组对象
				m_TaskGroup.StopLoginSteps();

				log_error("login step:%d, errorcode:%d",uStep, ret_code);

                qbBase::getMessageKernel()->NotifyNetStatus(uStep, ret_code);
            }
            break;
        }
    }

    void CAuthLoginImpl::startReLoginTimer(std::function<void()> task)
    {
		if (m_expired == false)
			return;

		m_expired = false;
		m_try_to_expire = false;

        std::thread([this, task]() mutable
        {
            int times = 0;
            int curTimeout = 0;
            int count = sizeof(sReloginTime) / sizeof(int);
            while (!m_try_to_expire)
            {
                if (curTimeout >= count)
                {
                    break;
                }
#ifdef WIN32
                ::Sleep(100);	// avoid sleep_until implementation bug.
#else
                std::this_thread::sleep_for(std::chrono::milliseconds(100));
#endif
                times++;
                if (times >= sReloginTime[curTimeout] * 10)
                {
                    curTimeout++;
                    times = 0;
                    task();
                }
            }
		    {
			    std::lock_guard<std::mutex> locker(m_mutex);
			    m_expired = true;
			    m_expired_cond.notify_one();
		    }
	    }).detach();
    }

    void CAuthLoginImpl::stopReLoginTimer()
    {
		if (m_expired || m_try_to_expire)
			return;

		m_try_to_expire = true;
		{
			std::unique_lock<std::mutex> locker(m_mutex);
			m_expired_cond.wait(locker, [this] { return m_expired == true; });
			m_try_to_expire = false;
		}
    }

    void CAuthLoginImpl::onTimer()
    {
        reLogin();
    }
}
